#include "gl_drawer.hpp"
/*
#include <gl/GL.h>
#include <gl/glu.h>
#include <gl/GLAux.h>
*/

#define APP_NAME "[gl drawer]"

GLDrawer::GLDrawer(QWidget* parent,
    QString file_vert_src_,
    QString file_frag_src_)
    :QOpenGLWidget(parent)
    , inited(false)
    , gl_point_size(4)
    , gl_program(nullptr)
    , logger(spdlog::default_logger())
    , file_vert_src(file_vert_src_)
    , file_frag_src(file_frag_src_)
{
    //set shader config file
    QString file_str(":/conf/opengl/");
    if(!file_vert_src.isEmpty()){
        QString file_tp = file_str;
        file_tp.append(file_vert_src);
        file_tp.append(".vert");
        file_vert_src = file_tp;
        logger->info("{} shader vert file {}",APP_NAME,file_vert_src.toStdString());
    }
    if(!file_frag_src.isEmpty()){
        QString file_tp = file_str;
        file_tp.append(file_frag_src);
        file_tp.append(".frag");
        file_frag_src = file_tp;
        logger->info("{} shader frag file {}",APP_NAME,file_frag_src.toStdString());
    }
    
    timer_update = new QTimer(this);
    mouse_ts = new MouseTrans(0.001,0.01,0.001);
    //设置透视矩阵
    matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
    //沿着Z轴负方向平移-2
    matrix.translate(0, 0, -2);
    //绕Y轴旋转
    matrix.rotate(1, 0, 1, 0);
    mouse_ts->init_matrix(matrix);

    QObject::connect(mouse_ts, &MouseTrans::sig_update, this, &GLDrawer::slot_mouse_update);

    QObject::connect(timer_update, &QTimer::timeout, [this]() {
        this->update();
        });
}

GLDrawer::~GLDrawer()
{
    delete mouse_ts;
    timer_update->deleteLater();
}

void GLDrawer::slot_mouse_update()
{
    matrix = mouse_ts->get_matrix();
    update();
}

void GLDrawer::normalize(const float scale)
{
    int idx = 0;
    Vec3d pos_mid;
    float max_val = 0;
    GLDataMap::iterator iter;
    for (iter = data_map.begin(); iter != data_map.end(); ++iter)
    {
        if (idx == 0) {
            pos_min = iter->second->pos_min;
            pos_max = iter->second->pos_max;
        }
        else {
            pt::update_min(pos_min, iter->second->pos_min);
            pt::update_max(pos_max, iter->second->pos_max);
        }
        idx++;
    }

    pos_mid.x = (pos_max.x + pos_min.x) / 2;
    pos_mid.y = (pos_max.y + pos_min.y) / 2;
    pos_mid.z = (pos_max.z + pos_min.z) / 2;

    if (max_val < (pos_max.x - pos_mid.x)) max_val = pos_max.x - pos_mid.x;
    if (max_val < (pos_max.y - pos_mid.y)) max_val = pos_max.y - pos_mid.y;
    if (max_val < (pos_max.z - pos_mid.z)) max_val = pos_max.z - pos_mid.z;

    idx = 0;
    float factor = scale / max_val;
    std::shared_ptr<GLData> data_tp;
    for (iter = data_map.begin(); iter != data_map.end(); ++iter)
    {
        data_tp = iter->second;
        for (int i = 0; i < data_tp->size_pos; ++i)
        {
            idx = data_tp->len_pos * i;
            data_tp->vertices[idx] -= pos_mid.x;
            data_tp->vertices[idx + 1] -= pos_mid.y;
            data_tp->vertices[idx + 2] -= pos_mid.z;
            data_tp->vertices[idx] *= factor;
            data_tp->vertices[idx + 1] *= factor;
            data_tp->vertices[idx + 2] *= factor;
        }
    }
}

void GLDrawer::add_data(std::shared_ptr<GLData>& data)
{
    clear_data(data->key);
    data_map[data->key] = data;
}

void GLDrawer::clear_data()
{
    GLDataMap::iterator iter;
    for (iter = data_map.begin(); iter != data_map.end(); ++iter)
    {
        iter->second->clear();
    }
    data_map.clear();
}

void GLDrawer::clear_data(const std::string& key)
{
    if (data_map.find(key) != data_map.end()) {
        data_map[key]->clear();
    }
}

void GLDrawer::start_timer()
{
    timer_update->start(10);
}

void GLDrawer::set_color_bg(const float color[4])
{
    bgcolor[0] = color[0];
    bgcolor[1] = color[1];
    bgcolor[2] = color[2];
    bgcolor[3] = color[3];
}

void GLDrawer::set_point_size(const int point_size_)
{
    gl_point_size = point_size_;
}

void GLDrawer::initializeGL()
{
    logger->info("{} initialize gl",APP_NAME);
    // 为当前环境初始化OpenGL函数
    initializeOpenGLFunctions();
    //新建着色器程序处理工具
    gl_program = new QOpenGLShaderProgram(this);
    //加载顶点着色器程序
    if(file_vert_src.isEmpty()){
        gl_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    }else{
        gl_program->addShaderFromSourceFile(QOpenGLShader::Vertex, file_vert_src);
    }
    //加载片段着色器程序
    if(file_frag_src.isEmpty()){
        gl_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    }else{
        gl_program->addShaderFromSourceFile(QOpenGLShader::Fragment, file_frag_src);
    }
    //链接着色器程序
    if (!gl_program->link()) {
        logger->info("{} gl program link failed", APP_NAME);
        return;
    }
    //绑定着色器程序
    if (!gl_program->bind()) {
        logger->info("{} gl program bind failed", APP_NAME);
        return;
    }
    //着色器程序位置属性
    gl_attr_pos = gl_program->attributeLocation("posAttr");
    //断言是否成功
    Q_ASSERT(gl_attr_pos != -1);
    //着色器程序颜色属性
    gl_attr_color = gl_program->attributeLocation("colAttr");
    //断言是否成功
    Q_ASSERT(gl_attr_color != -1);
    //返回统一值
    gl_uniform_matrix = gl_program->uniformLocation("matrix");
    //断言是否成功
    Q_ASSERT(gl_uniform_matrix != -1);
    logger->info("{} initialize gl success",APP_NAME);
    inited = true;
}

void GLDrawer::resizeGL(int width, int height)
{
	//获取设备像素比
    const qreal retinaScale = devicePixelRatio();
    //设置视口
    //glViewport(0, 0, width * retinaScale, height * retinaScale);
    glViewport(0, 0, width, height);
    logger->info("{} resize gl {} {} {}",APP_NAME,height,width,retinaScale);
}

void GLDrawer::paintGL()
{
    //设置背景色
    glClearColor(bgcolor[0], bgcolor[1], bgcolor[2], bgcolor[3]);
    //清空颜色缓存，深度缓存，模板缓存
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    //点大小
    glPointSize(gl_point_size);
    //绑定着色器
    gl_program->bind();
    //为着色器程序传入当前变换
    gl_program->setUniformValue(gl_uniform_matrix, matrix);

    GLDataMap::iterator iter;
    std::shared_ptr<GLData> data;
    for (iter = data_map.begin(); iter != data_map.end(); iter++)
    {
        data = iter->second;
        if (nullptr == data->vertices) {
            continue;
        }
        //为着色器设置顶点属性
        glVertexAttribPointer(gl_attr_pos, data->len_pos, GL_FLOAT, GL_FALSE, 0, data->vertices);
        //为着色器设置颜色属性
        glVertexAttribPointer(gl_attr_color, data->len_color, GL_FLOAT, GL_FALSE, 0, data->colors);

        //启用顶点着色器属性
        glEnableVertexAttribArray(gl_attr_pos);
        //启用颜色着色器属性
        glEnableVertexAttribArray(gl_attr_color);
          
        //绘制一维数组数据,size_pose是顶点的数量
        glDrawArrays(data->mode, 0, data->size_pos);
        
        //关闭顶点着色器属性
        glDisableVertexAttribArray(gl_attr_color);
        //关闭颜色着色器属性
        glDisableVertexAttribArray(gl_attr_pos);
    }
    //解绑着色器
    gl_program->release();
    logger->info("{} paint gl end", APP_NAME);
    m_frame++;
}

/*
void GLDrawer::merge_data()
{
    if (nullptr != colors) {
        delete[] colors;
        colors = nullptr;
    }
    if (nullptr != vertices) {
        delete[] vertices;
        vertices = nullptr;
    }
    int len_total_pos = 0;
    int len_total_col = 0;
    GLDataMap::iterator iter;
    std::shared_ptr<GLData> data;
    for (iter = data_map.begin(); iter != data_map.end(); iter++)
    {
        data = iter->second;
        if (nullptr == data->vertices) {
            continue;
        }
        len_total_pos + = data->size * data->len_pos;
        len_total_col + = data->size * data->len_color;
    }
    colors = (GLfloat*)calloc(len_total_col, sizeof(GLfloat));
    vertices = (GLfloat*)calloc(len_total_pos, sizeof(GLfloat));

    int idx_pos = 0;
    int idx_col = 0;
    for (iter = data_map.begin(); iter != data_map.end(); iter++)
    {
        data = iter->second;
        if (nullptr == data->vertices) {
            continue;
        }
        int len_pos = data->size * data->len_pos;
        int len_col = data->size * data->len_color;
        for (int i = 0; i < len_pos; ++i) {
            vertices[idx_pos] = data->vertices[i];
            idx_pos++;
        }
        for (int i = 0; i < len_col; ++i) {
            colors[idx_col] = data->colors[i];
            idx_col++;
        }
    }
}
*/

void GLDrawer::mousePressEvent(QMouseEvent *event)
{
    mouse_ts->press(event);
}

void GLDrawer::mouseMoveEvent(QMouseEvent *event)
{
    mouse_ts->move(event);
}

void GLDrawer::wheelEvent(QWheelEvent *event)
{
    mouse_ts->wheel(event);
}